package com.easylinkin.linkappapi.security.config;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.easylinkin.bases.redis.util.RedisUtil;
import com.easylinkin.linkappapi.common.constant.CommonConstant;
import com.easylinkin.linkappapi.common.constant.CommonConstant.Platform;
import com.easylinkin.linkappapi.common.utils.HeaderMapRequestWrapper;
import com.easylinkin.linkappapi.operatelog.entity.CommonOperateLog;
import com.easylinkin.linkappapi.operatelog.service.CommonOperateLogService;
import com.easylinkin.linkappapi.security.context.LinkappUserContextProducer;
import com.easylinkin.linkappapi.security.entity.LinkappUser;
import com.easylinkin.linkappapi.security.service.LinkappUserLoginService;
import com.easylinkin.linkappapi.security.service.LinkappUserService;
import com.easylinkin.linkappapi.security.utils.RSAUtils;
import com.easylinkin.linkappapi.tenant.entity.LinkappTenant;
import com.easylinkin.linkappapi.tenant.sevice.LinkappTenantService;
import com.easylinkin.security.service.SmLoginService;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.web.filter.OncePerRequestFilter;
import site.morn.boot.web.Responses;
import site.morn.rest.RestBuilders;
import site.morn.rest.RestMessage;
import site.morn.util.MessageDigestUtils;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.Date;
import java.util.List;

import static site.morn.constant.DigestConstant.Algorithms.SPRING_B_CRYPT;

@Component
public class AuthenticationTokenFilter extends OncePerRequestFilter {

  private static final Logger log = LoggerFactory.getLogger(AuthenticationTokenFilter.class);

  // 用户信息service
  @Resource
  private LinkappUserLoginService LinkappUserLoginService;
  //	@Resource
//	private LinkappUserMapper linkappUserMapper;
  @Resource
  private LinkappUserService linkappUserService;
  @Resource
  private LinkappTenantService linkappTenantService;
  @Resource
  LoginCountCheck loginCountCheck;
  @Resource
  private CommonOperateLogService commonOperateLogService;
  @Resource
  private SmLoginService smLoginService;

  @Autowired
  RedisUtil redisUtil;

  @Resource
  private LinkappUserContextProducer linkappUserContextProducer;


  @Override
  protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
      FilterChain filterChain)
      throws ServletException, IOException {


    String platform = request.getParameter("platform");
    if(StringUtils.isNotBlank(platform)){
      platform = URLUtil.decode(platform);
    }

    String tenantId = request.getHeader("token");
    if(StringUtils.isNotBlank(tenantId)){
      tenantId = URLUtil.decode(tenantId);
    }
    String appToken = request.getHeader("app-token");
    if(StringUtils.isNotBlank(appToken)){
      appToken = URLUtil.decode(appToken);
    }

    String username = request.getParameter("username");
    if(StringUtils.isNotBlank(username)){
      username = URLUtil.decode(username);
    }
    String password = request.getParameter("password");
    if(StringUtils.isNotBlank(password)){
      // request.getParameter获得的参数已经过URLEncode解码了，不能重复解码
      // password = URLUtil.decode(password);
      try {
        // 2024-12-04，现在只有web项目端加密，先不区分都解密，影响性能再优化
        password = RSAUtils.decrypt(password);
      } catch (Exception ignored) {
      }
    }
    if (StringUtils.isNotBlank(username)) {
      platform = Platform.app.getName();
    }

    if (StringUtils.isBlank(platform)) {
      platform = request.getHeader("platform");
    }

    if (StringUtils.isBlank(platform)) {
      platform = Platform.UNKNOWN_PLATFORM.getName();
    }

    if (platform.equals(Platform.app.getName()) && "/login".equals(request.getRequestURI())) {
      //app登录
      boolean appLoginOk = appUserLogin(request, response);

      if (!appLoginOk) {
        //用户名不为空，再用用户名 + 密码登录校验
        if (StringUtils.isBlank(username)) {
          return;
        }
        //清除手机号 登录返回的异常
        response.resetBuffer();
        request.setAttribute("username", username);
        request.setAttribute("password", password);
      }else{
        Object usernameObj = request.getAttribute("username");
        if (usernameObj != null){
          username = usernameObj.toString();
        }
      }
    }

//		如果是登录接口 校验用户是否被锁定
    if ("/login".equals(request.getRequestURI())
        || "/adminPhoneLogin".equals(request.getRequestURI())
        || "/meterLogin".equals(request.getRequestURI())
        || "/meterCodeLogin".equals(request.getRequestURI())
        || "/adminUser/verificationCode".equals(request.getRequestURI())) {
      if (StringUtils.isBlank(username)) {
        return;
      }
      boolean locked = loginCountCheck.checkUserLocked(username);
      if (locked) {
        SecurityContextHolder.getContext().setAuthentication(null);
        RestMessage message = RestBuilders.failureMessage("login.failure").setMessage("用户已被锁");
        Responses.standard(response).respond(message);
        CommonOperateLog commonOperateLog = new CommonOperateLog();
        commonOperateLog.setModuleName("登录");
        commonOperateLog.setContent("用户被锁");
        commonOperateLog.setUserAccount(request.getParameter("username"));
        if ("/login".equals(request.getRequestURI())) {
          LinkappUser user = linkappUserService.findByUsername(username);
          if (user != null) {
            commonOperateLog.setTenantId(user.getTenantId());
          }
        } else {
          commonOperateLog.setTenantId("operation_management_user");
        }
        commonOperateLog.setCreateTime(new Date()).setResult(false);
        commonOperateLogService.addLog(commonOperateLog);
        return;
      }
    }

//		LinkappUser user = null;
    //String tenantId = request.getHeader("token");

    if (ObjectUtils.isEmpty(tenantId)) {
//			user 里面放置的项目id,项目id找到对应的租户id 找到默认admin用户，内部做一次登录
      String projectId = request.getHeader("user");
      if (ObjectUtils.isNotEmpty(projectId)) {
        QueryWrapper<LinkappTenant> qw = new QueryWrapper<>();
        qw.select("id");
        qw.eq("project_id", projectId);
        List<LinkappTenant> tenants = linkappTenantService.list(qw);
        tenantId = tenants.size() > 0 ? tenants.get(0).getId() : null;
      }
    }

    if (ObjectUtils.isNotEmpty(tenantId)) {
      if (platform.equals(Platform.UNKNOWN_PLATFORM.getName())) {
        //如果是非app登录  （app登录，不需要找管理员登录）
        username = linkappUserService.findAdminTypeUserNameByTenantId(tenantId);
        if (username != null) {
          // 根据用户名获取用户对象
          UserDetails userDetails = LinkappUserLoginService.loadUserByUsername(username);
          UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
              userDetails, null,
              userDetails.getAuthorities());
          authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
          // 设置为已登录
          SecurityContextHolder.getContext().setAuthentication(authentication);
          HttpSession session = request.getSession(false);
          if (session != null) {
            session.setMaxInactiveInterval(CommonConstant.SESSION_TIME_DEFAULT);
          }
        }
      }
      if (platform.equals(Platform.app.getName()) && StringUtils.isNotBlank(appToken)) {
        //app登录
        //TODO 增加appToken机制，支持app恢复（以后处理）
        String appUserSessionKey = CommonConstant.appTokenKeyPre + appToken;

        Object appSessionUser = redisUtil.get(appUserSessionKey);
        if (appSessionUser != null) {
          //appToken解密
          // 解密为字符串
          String decryptStr = SecureUtil.aes(CommonConstant.AesKey.getBytes())
              .decryptStr(appToken, CharsetUtil.CHARSET_UTF_8);
          LinkappUser curUser = JSONUtil.toBean(appSessionUser.toString(), LinkappUser.class);
          String curUserName = curUser.getUsername();
          UserDetails userDetails = LinkappUserLoginService.loadUserByUsername(curUserName);
          UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
              userDetails, null,
              userDetails.getAuthorities());
          authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
          // 设置为已登录
          SecurityContextHolder.getContext().setAuthentication(authentication);
          HttpSession session = request.getSession(false);
          if (session != null) {
            session.setMaxInactiveInterval(CommonConstant.SESSION_TIME_DEFAULT);
          }
          /*
          Authentication authent = SecurityContextHolder.getContext().getAuthentication();
          if (authentication != null && !(authent instanceof AnonymousAuthenticationToken)) {
            String currentUserName = authent.getName();
            extendLoginInfo(currentUserName, request);
          } else {
            throw new RuntimeException("用户登录信息失效");
          }
          */
        }

      }
    }

    //		支持运营端的用户做内部登录 开放接口使用
    String adminUserName = request.getHeader("token-admin");
    if (ObjectUtils.isNotEmpty(adminUserName)) {
      Assert.isNull(request.getHeader("token"), "token 与 token-admin参数不能同时存在");
      // 根据用户名获取用户对象
      UserDetails userDetails = smLoginService.loadUserByUsername(adminUserName);
      UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
          userDetails, null,
          userDetails.getAuthorities());
      authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
      // 设置为已登录
      SecurityContextHolder.getContext().setAuthentication(authentication);
      HttpSession session = request.getSession(false);
      if (session != null) {
        session.setMaxInactiveInterval(CommonConstant.SESSION_TIME_DEFAULT);
      }

    }

//			如果请求头里面携带了 想做认证的token  但是信息错误就做一次登出
//		if (request.getHeader("token") != null || request.getHeader("user") != null) {
//			if (user == null) {
//				SecurityContextHolder.getContext().setAuthentication(null);
//			}
//		}
    if (platform.equals(Platform.app.getName()) && "/login".equals(request.getRequestURI())) {
      HttpServletRequest hrequest = (HttpServletRequest) request;
      HeaderMapRequestWrapper parameterRequestWrapper = new HeaderMapRequestWrapper(hrequest);
      username = (String) request.getAttribute("username");
      password = (String) request.getAttribute("password");
      parameterRequestWrapper.addParameter("username", username);
      parameterRequestWrapper.addParameter("password", password);

      filterChain.doFilter(parameterRequestWrapper, response);
    } else {
      filterChain.doFilter(request, response);
    }
  }

  /**
   * app用户登录 手机号 + 验证码/手机号 + 密码
   *
   * @param request  对象
   * @param response 对象
   * @return 验证结果
   */
  private boolean appUserLogin(HttpServletRequest request, HttpServletResponse response) {
    String phone = request.getParameter("phone");
    if(StringUtils.isNotBlank(phone)){
      phone = URLUtil.decode(phone);
    }
    String username = request.getParameter("username");
    if(StringUtils.isNotBlank(username)){
      username = URLUtil.decode(username);
    }
    String password = request.getParameter("password");
    if(StringUtils.isNotBlank(password)){
      // password = URLUtil.decode(password);
      try {
        password = RSAUtils.decrypt(password);
      } catch (Exception ignored) {}
    }
    String verificationCode = request.getParameter("verificationCode");
    if(StringUtils.isNotBlank(verificationCode)){
      verificationCode = URLUtil.decode(verificationCode);
    }
    String tenantId = request.getHeader("token");
    if (StringUtils.isBlank(phone)) {
      RestMessage message = RestBuilders.failureBuilder().code("login.failure")
          .message("手机号为空").build();
      Responses.standard(response).respond(message);
      return false;
    }
    //1、根据登录用户名称查询所有用户
    List<LinkappUser> linkappUserLs = queryLinkappUserByPhone(phone);
    if (CollectionUtil.isEmpty(linkappUserLs)) {
      RestMessage message = RestBuilders.failureBuilder().code("login.failure")
          .message("用户不存在").build();
      Responses.standard(response).respond(message);
      return false;
    }

    boolean checkPass = false;
    String userPassword = "";

    //20220708cp要求提示明确
    String loginType = null;
//    LinkappUser user = linkappUserService.findByUsername(username);
    String platform1 = request.getHeader("platform");
    if (StringUtils.isBlank(verificationCode)) {
      loginType = "pwd";
      //手机号 + 密码登录（一个用户密码匹配就表示通过）
      for (int i = 0; i < linkappUserLs.size(); i++) {
        LinkappUser linkappUser = linkappUserLs.get(i);
        if (MessageDigestUtils.matches(SPRING_B_CRYPT, password, linkappUser.getPassword())) {
          username = linkappUser.getUsername();
          tenantId = linkappUser.getTenantId();
          userPassword = linkappUser.getPassword();
          checkPass = true;
          request.setAttribute("username",username);
          break;
        }
      }

    } else {
      //手机号验证码登录
      loginType = "code";
      LinkappUser linkappUser = linkappUserLs.get(0);
      Object phoneCodeObj = redisUtil.get(phone);
      if (phoneCodeObj != null) {
        String phoneCode = phoneCodeObj.toString();
        if (phoneCode.equals(verificationCode)) {
          username = linkappUser.getUsername();
          tenantId = linkappUser.getTenantId();
          userPassword = linkappUser.getPassword();
          checkPass = true;
        }
      }
    }

    String content = (loginType.equals("pwd")?"账号":"短信");
    if (checkPass) {
      // 记录登录成功
      if(StringUtils.isNotEmpty(platform1)&&Platform.app.getName().equals(platform1)){
        loginOperatorLog(linkappUserLs.get(0),content + "登录成功",platform1,true);
      }
      extendLoginInfo(username, request);
      request.setAttribute("username", username);
      request.setAttribute("password", userPassword);
      return true;
    } else {
      // 记录登录失败
      if(StringUtils.isNotEmpty(platform1)&&Platform.app.getName().equals(platform1)){
        loginOperatorLog(linkappUserLs.get(0),content + "登录失败",platform1,false);
      }
      String msg = "用户登录校验不通过";
      if (StringUtils.isNotBlank(loginType)) {
        switch (loginType) {
          case "pwd":
            msg = "密码不正确";
            break;
          case "code":
            msg = "验证码不正确或已失效";
            break;
          default:
            break;
        }
      }

      RestMessage message = RestBuilders.failureBuilder().code("login.failure")
          .message(msg).build();
      Responses.standard(response).respond(message);
      return false;
    }


  }

  /**
   * 根据手机号查询所有用户
   *
   * @param phone 手机号
   * @return 用户list
   */
  private List<LinkappUser> queryLinkappUserByPhone(String phone) {
    LinkappUser linkappUserExmaple = new LinkappUser();
    linkappUserExmaple.setPhone(phone);
    //20220711变更:排除已冻结的账号 bug:1012551
    linkappUserExmaple.setLocked(false);
    List<LinkappUser> linkappUserLs = linkappUserService.selectUsers(linkappUserExmaple);
    return linkappUserLs;
  }

  /**
   * 延迟登录信息
   *
   * @param username 用户名
   * @param request  request对象
   */
  private void extendLoginInfo(String username, HttpServletRequest request) {
    // 根据用户名获取用户对象
    UserDetails userDetails = LinkappUserLoginService.loadUserByUsername(username);
    UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
        userDetails, null,
        userDetails.getAuthorities());
    authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
    // 设置为已登录
    SecurityContextHolder.getContext().setAuthentication(authentication);
    HttpSession session = request.getSession(false);
    if (session != null) {
      session.setMaxInactiveInterval(CommonConstant.SESSION_TIME_DEFAULT);
    }
  }

  private void loginOperatorLog(LinkappUser user,String content,String platform1,Boolean flag){
    CommonOperateLog commonOperateLog = new CommonOperateLog();
    if (user != null) {
      commonOperateLog.setUserAccount(user.getUsername());
      commonOperateLog.setTenantId(user.getTenantId());
      commonOperateLog.setPhone(user.getPhone());
      commonOperateLog.setNickname(user.getNickname());
    }
    if(StringUtils.isNotEmpty(platform1)){
      commonOperateLog.setPlatform(platform1);
    }
    commonOperateLog.setModuleName("登录");
    commonOperateLog.setContent(content);
    commonOperateLog.setCreateTime(new Date()).setResult(flag);
    commonOperateLogService.addLog(commonOperateLog);
  }

}
